home *** CD-ROM | disk | FTP | other *** search
/ Power Programmierung 2 / Power-Programmierung CD 2 (Tewi)(1994).iso / gnu / djgpp / src / libgplus.5 / libgplus / etc / adt-exam / tsort.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1993-01-15  |  8.2 KB  |  300 lines

  1. /* Date: Sun, 18 Sep 88 15:57:31 -0700 
  2.    From: "Douglas C. Schmidt" <schmidt%crimee.ics.uci.edu@ORION.CF.UCI.EDU> 
  3.  
  4.    The following tool performs topological sorting in O(n + m) average-time
  5.    (n == # of vertices, m == # of edges).  
  6.  
  7.    If you happen to have Andrew Koenig's Summer USENIX '88 article on
  8.    Associative Arrays in C++ it is interesting to compare his solution to
  9.    mine.  Basically, my version, which uses hashing as opposed to his AVL
  10.    trees, should be *much* faster on the average (it would be interesting
  11.    to compare the two).  In fact, if you test the code below against the
  12.    standard UNIX tsort routine, you should be *amazed* at how much faster
  13.    it is (several orders of magnitude on large input files)! */
  14.  
  15. #include <stream.h>
  16. #include <std.h>
  17. #include <builtin.h>
  18. #include <String.h>
  19.  
  20. /* Forward class decls. */
  21. class Vertex_Node;  
  22. class Assoc_Array;
  23. class Assoc_Iter;
  24.  
  25. /* Defines and implements the adjacency list abstraction that contains the list
  26.    of vertices that are incident to given vertex.  Note that we store Vertex_Node
  27.    pointers here, rather than vertex *Names*.  This allows us to find a particular
  28.    vertex in the Hash_Table in O(1) time, without having to perform the usual
  29.    hash lookup (compare this with Andrew Koenig's implementation from the Summer
  30.    USENIX '88 proceedings!) */
  31.  
  32. class Adjacency_Node 
  33. {
  34. private:
  35.   Vertex_Node    *item_ptr;
  36.   Adjacency_Node *next_ptr;
  37.   
  38. public:
  39.   
  40.   /* The constructor simply orders the adjacency list as a stack. */
  41.   Adjacency_Node (Vertex_Node *new_item, Adjacency_Node *head):
  42.        item_ptr (new_item), next_ptr (head) {}
  43.   
  44.   Vertex_Node *item (void) 
  45.     {
  46.       return item_ptr;
  47.     }
  48.   
  49.   Adjacency_Node *next (void) 
  50.     {
  51.       return next_ptr;
  52.     }
  53. };
  54.  
  55. /* The Hash_Table is composed of these Vertex_Nodes.  Each Vertex_Node contains
  56.    an adjacency list of successor nodes, and an In_Degree count.  Each operation
  57.    is extremely concise and self-explanatory, with the exception of the overloaded
  58.    ``+='' operator, which essentially means ``Add the successor node to the successor
  59.    list (OK, so this is an abuse of overloading!) */
  60.  
  61. class Vertex_Node 
  62. {
  63. friend class Assoc_Array;
  64. friend class Assoc_Iter;
  65.  
  66. private:
  67.   int             in_degree;
  68.   String          name;
  69.   Adjacency_Node *succ_list;
  70.   Vertex_Node    *next;
  71.   
  72. public:
  73.   Vertex_Node (String &item, Vertex_Node *n = 0):
  74.        in_degree (0), name (item), next (n), succ_list (0) {}
  75.   
  76.   int in_degree_count (void) 
  77.     {
  78.       return in_degree;
  79.     }
  80.   
  81.   Adjacency_Node *get_succ_list (void) 
  82.     {
  83.       return succ_list;
  84.     }
  85.   
  86.   Vertex_Node& operator++ (void)
  87.     {
  88.       in_degree++;
  89.       return *this;
  90.     }
  91.   
  92.   int operator-- (void)
  93.     {
  94.       return --in_degree;
  95.     }
  96.   
  97.   Vertex_Node& operator+= (Vertex_Node& succ_node)
  98.     {
  99.       succ_list = new Adjacency_Node (&succ_node,succ_list);
  100.       return *this;
  101.     }
  102.   
  103.   String &item (void) 
  104.     {
  105.       return name;
  106.     }
  107.   
  108.   friend ostream& operator << (ostream& stream,Vertex_Node& output)
  109.     {
  110.       return stream << output.in_degree;
  111.     }
  112.   
  113. };
  114.  
  115. /* Here's the main data structure.  It is simply a Hash_Table of Vertex_Nodes.
  116.    I make use of hashpjw from the libg++ builtin library.  Nothing too tricky
  117.    here....  I overload the [] operator to provide the classic ``associative
  118.    array'' touch. */
  119.  
  120. class Assoc_Array
  121. {
  122. friend class Assoc_Iter;
  123.  
  124. private:
  125.   int           total_size;
  126.   int           current_size;
  127.   Vertex_Node **hash_table;
  128.   
  129. public:
  130.   Assoc_Array (int size): total_size (size), current_size (0)
  131.     {
  132.       hash_table = new Vertex_Node *[size];         
  133.       memset (hash_table, 0, size * sizeof *hash_table);
  134.     }
  135.   
  136.   Vertex_Node& operator[](String index)
  137.     {
  138.       int          location = hashpjw (index) % total_size;
  139.       Vertex_Node *head     = hash_table[location];
  140.       
  141.       if (!head)
  142.         {
  143.           current_size++;
  144.           return *(hash_table[location] = new Vertex_Node (index));
  145.         }
  146.       else 
  147.         {
  148.           
  149.           for ( ; head; head = head->next)
  150.             if (head->name == index)
  151.               return *head;
  152.           
  153.           current_size++;
  154.           return *(hash_table[location] = new Vertex_Node (index, hash_table[location]));
  155.         }
  156.     }
  157.   
  158.   int array_size (void)
  159.     {
  160.       return current_size;
  161.     }
  162.   
  163.   int max_size (void)
  164.     {
  165.       return total_size;
  166.     }
  167. };
  168.  
  169. /* This is a neat class, which implements an iterator for the Hash_Table used
  170.    for the associative array.  It would be faster to search the table directly,
  171.    without using an iterator, but this is a neat feature of C++ that fits too
  172.    nicely into the overall abstraction to pass up! */
  173.  
  174. class Assoc_Iter
  175. {
  176. private:
  177.   Vertex_Node *curr;
  178.   Assoc_Array *assoc;
  179.   int          curr_table_pos;
  180.   
  181. public:
  182.   Assoc_Iter (Assoc_Array& array,int items): 
  183.        assoc (&array), curr (0), curr_table_pos (items - 1) {}
  184.   
  185.   Vertex_Node *operator() (void)
  186.     {
  187.       if (curr_table_pos <= 0)  /* we're at the end of the table, quit. */
  188.         return 0;
  189.       else if (!curr)
  190.         {                       /* need to find a new bucket list to search. */
  191.           
  192.           while (curr_table_pos >= 0 && !assoc->hash_table[curr_table_pos])
  193.             curr_table_pos--;
  194.           
  195.           if (curr_table_pos < 0)
  196.             return 0;
  197.           else
  198.             curr = assoc->hash_table[curr_table_pos];
  199.         }
  200.       
  201.       Vertex_Node *temp = curr;
  202.       if (!(curr = curr->next)) /* try to update curr for next call. */
  203.         curr_table_pos--;
  204.       return temp;
  205.     }
  206. };
  207.  
  208. /* This is a very simple stack abstraction. Doesn't perform error checking. */
  209.  
  210. class Node_Stack
  211. {
  212. private:
  213.   int           top;
  214.   int           max_node_stack_size;
  215.   Vertex_Node **node_stack_items;
  216.   
  217. public:
  218.   Node_Stack (int size): top (size), max_node_stack_size (size)
  219.     {
  220.       node_stack_items = new Vertex_Node *[size];
  221.     }
  222.   
  223.   int empty ()
  224.     {
  225.       return top >= max_node_stack_size;
  226.     }
  227.   
  228.   void push (Vertex_Node *item)
  229.     {
  230.       node_stack_items[--top] = item;
  231.     }
  232.   
  233.   Vertex_Node *pop (void)
  234.     {
  235.       return node_stack_items[top++];
  236.     }
  237. };
  238.  
  239. const int DEFAULT_SIZE = 200;
  240. const int BUF_SIZE = 200;
  241.  
  242. main (int argc, char **argv)
  243. {
  244.   Assoc_Array assoc_array (argc > 1 ? atoi (argv[1]) : DEFAULT_SIZE);
  245.   String prev;
  246.   String succ;
  247.   
  248.   /* The use of ``+='' below is rather obscure.  It simply means ``prepend the
  249.      address of A[Succ] to the adjacency list of A[Prev].  See the comment in
  250.      the member definition for class Vertex_Node.  Note that the ++ operator
  251.      has also been overloaded.  This allows concise, extremely efficient mechanism
  252.      to enter the Prev and Succ items into the associative array. */
  253.   
  254.   while (cin >> prev >> succ) 
  255.     if (prev == succ)       /* Just record existence of identical tokens. */
  256.       assoc_array[prev];
  257.     else 
  258.       assoc_array[prev] += ++assoc_array[succ];
  259.   
  260.   Assoc_Iter   next (assoc_array, assoc_array.max_size ());
  261.   Node_Stack   stack (assoc_array.array_size ());
  262.   Vertex_Node *node_ptr;
  263.   
  264.   /* Iterate through all the items in the associative array, performing the
  265.      typical topological sort trick of pushing all ``sources'' onto a stack.
  266.      This gives us an order of magnitude win over the standard UNIX tsort
  267.      routine, which has quadratic average time complexity (in the number of
  268.      verticies!!!!). */
  269.   
  270.   while (node_ptr = next ()) 
  271.     if (node_ptr->in_degree_count () == 0) 
  272.       stack.push (node_ptr);
  273.   
  274.   /* The following code is straight-forward, but the one line with the expression
  275.      --(*List->Item ()) == 0 is rather cryptic.  It is simply dereferencing a pointer
  276.      to a Vertex_Node, and then passing this by reference to the overloaded --
  277.      operator from the Vertex_Node class (yes, it *is* easy to abuse this
  278.      technique!). */
  279.   
  280.   for (int items = 0; !stack.empty (); items++)
  281.     {
  282.       node_ptr = stack.pop ();
  283.       cout << node_ptr->item () << "\n";
  284.       
  285.       for (Adjacency_Node *list = node_ptr->get_succ_list (); list; list = list->next ())
  286.         if (--*list->item () == 0) 
  287.           stack.push (list->item ());                         
  288.       
  289.     }
  290.   
  291.   if (items != assoc_array.array_size ())
  292.     {
  293.       cerr << argv[0] << ": error, directed graph has a cycle!\n";
  294.       return 1;
  295.     }
  296.   else
  297.     return 0;
  298. }
  299.  
  300.